﻿#define _CRT_SECURE_NO_WARNINGS

#define HAVE_REMOTE
#define WIN32
#define WPCAP
#pragma warning(default : 4005)
#pragma comment(lib, "wpcap.lib")
#pragma comment(lib, "Packet.lib")
#pragma comment(lib, "ws2_32.lib")

#include <stdio.h>
#include <time.h>
#include <stdbool.h>
#include <string.h>
#include <pcap.h>


#define SIZE_ETHERNET 14  // 以太网头的固定大小为14字节

bool capture_next_packet = false;  // 全局变量，用于标识是否捕获下一个数据包
char last_username[100] = { 0 };   // 存储上一次 USER 命令的用户名

// 定义以太网头结构
struct sniff_ethernet {
    u_char ether_dhost[6];  // 目标主机 MAC 地址
    u_char ether_shost[6];  // 源主机 MAC 地址
    u_short ether_type;     // 协议类型（如 IP、ARP、RARP 等）
};

// 定义 IP 报头结构
struct sniff_ip {
    u_char ip_vhl;   // 版本和报头长度（4位版本 + 4位报头长度）
    u_char ip_tos;   // 服务类型
    u_short ip_len;  // 总长度
    u_short ip_id;   // 标识符
    u_short ip_off;  // 分片偏移字段
    u_char ip_ttl;   // 生存时间
    u_char ip_p;     // 协议类型
    u_short ip_sum;  // 校验和
    u_char ip_src[4];  // 源 IP 地址
    u_char ip_dst[4];  // 目标 IP 地址
};

// 定义 TCP 报头结构
typedef u_int tcp_seq;  // TCP 序列号的类型定义

struct sniff_tcp {
    u_short th_sport;  // 源端口
    u_short th_dport;  // 目标端口
    tcp_seq th_seq;    // 序列号
    tcp_seq th_ack;    // 确认号
    u_char th_offx2;   // 数据偏移
    u_char th_flags;   // 标志位
    u_short th_win;    // 窗口大小
    u_short th_sum;    // 校验和
    u_short th_urp;    // 紧急指针
};

// 枚举 FTP 状态，用于表示当前的解析状态
enum State {
    CAPTURE_USER,  // 捕获用户名状态
    CAPTURE_PASS,  // 捕获密码状态
    CAPTURE_STATUS  // 捕获登录状态
};

// 定义 FTP 会话结构体，用于存储用户名、密码和状态
struct FTPSession {
    char username[100];  // 用户名
    char password[100];  // 密码
    char status[10];     // 登录状态（SUCCEED 或 FAILED）
    enum State state;    // 当前解析的状态
};

// 初始化一个全局的 FTP 会话
struct FTPSession currentSession = { "", "", "UNKNOWN", CAPTURE_USER };

// 数据包处理函数
void packet_handler(u_char* args, const struct pcap_pkthdr* header, const u_char* packet) {
    const struct sniff_ethernet* ethernet = (struct sniff_ethernet*)(packet);  // 获取以太网头
    const struct sniff_ip* ip = (struct sniff_ip*)(packet + SIZE_ETHERNET);  // 获取 IP 头
    int size_ip = (ip->ip_vhl & 0x0f) * 4;  // IP 报头长度
    const struct sniff_tcp* tcp = (struct sniff_tcp*)(packet + SIZE_ETHERNET + size_ip);  // 获取 TCP 头
    int size_tcp = ((tcp->th_offx2 & 0xf0) >> 4) * 4;  // TCP 报头长度
    char* payload = (char*)(packet + SIZE_ETHERNET + size_ip + size_tcp);  // 获取 TCP 数据负载部分
    int size_payload = ntohs(ip->ip_len) - (size_ip + size_tcp);  // 数据负载的大小
    FILE* log_file = (FILE*)args;  // 日志文件指针

    char time_buffer[30];  // 用于存储格式化的时间
    struct tm* ltime;
    time_t local_tv_sec;

    local_tv_sec = header->ts.tv_sec;  // 捕获的时间戳
    ltime = localtime(&local_tv_sec);  // 转换为本地时间
    strftime(time_buffer, sizeof(time_buffer), "%Y-%m-%d %H:%M:%S", ltime);  // 格式化时间

    if (size_payload > 0) {
        payload[size_payload] = '\0';  // 确保数据以空字符结尾

        // 根据当前会话状态处理不同的 FTP 命令
        switch (currentSession.state) {
        case CAPTURE_USER:
            if (strstr(payload, "USER")) {  // 检查 USER 命令
                sscanf(strstr(payload, "USER"), "USER %s", currentSession.username);  // 提取用户名
                currentSession.state = CAPTURE_PASS;  // 进入密码捕获状态
            }
            break;

        case CAPTURE_PASS:
            if (strstr(payload, "PASS")) {  // 检查 PASS 命令
                sscanf(strstr(payload, "PASS"), "PASS %s", currentSession.password);  // 提取密码
                currentSession.state = CAPTURE_STATUS;  // 进入状态捕获状态
            }
            break;

        case CAPTURE_STATUS:
            if (strstr(payload, "230")) {  // 检查登录成功的响应
                strcpy(currentSession.status, "SUCCEED");  // 登录成功
            }
            else if (strstr(payload, "530")) {  // 检查登录失败的响应
                strcpy(currentSession.status, "FAILED");  // 登录失败
            }

            // 将结果写入日志文件
            fprintf(log_file, "%s,%02x:%02x:%02x:%02x:%02x:%02x,%d.%d.%d.%d,%02x:%02x:%02x:%02x:%02x:%02x,%d.%d.%d.%d,%s,%s,%s\n",
                time_buffer,
                ethernet->ether_shost[0], ethernet->ether_shost[1], ethernet->ether_shost[2],
                ethernet->ether_shost[3], ethernet->ether_shost[4], ethernet->ether_shost[5],
                ip->ip_src[0], ip->ip_src[1], ip->ip_src[2], ip->ip_src[3],
                ethernet->ether_dhost[0], ethernet->ether_dhost[1], ethernet->ether_dhost[2],
                ethernet->ether_dhost[3], ethernet->ether_dhost[4], ethernet->ether_dhost[5],
                ip->ip_dst[0], ip->ip_dst[1], ip->ip_dst[2], ip->ip_dst[3],
                currentSession.username, currentSession.password, currentSession.status);

            // 在控制台打印结果
            printf("%s,%02x:%02x:%02x:%02x:%02x:%02x,%d.%d.%d.%d,%02x:%02x:%02x:%02x:%02x:%02x,%d.%d.%d.%d,%s,%s,%s\n",
                time_buffer,
                ethernet->ether_shost[0], ethernet->ether_shost[1], ethernet->ether_shost[2],
                ethernet->ether_shost[3], ethernet->ether_shost[4], ethernet->ether_shost[5],
                ip->ip_src[0], ip->ip_src[1], ip->ip_src[2], ip->ip_src[3],
                ethernet->ether_dhost[0], ethernet->ether_dhost[1], ethernet->ether_dhost[2],
                ethernet->ether_dhost[3], ethernet->ether_dhost[4], ethernet->ether_dhost[5],
                ip->ip_dst[0], ip->ip_dst[1], ip->ip_dst[2], ip->ip_dst[3],
                currentSession.username, currentSession.password, currentSession.status);

            // 重置会话以捕获下一个登录操作
            memset(&currentSession, 0, sizeof(currentSession));
            currentSession.state = CAPTURE_USER;
            break;
        }
    }
}

int main() {
    pcap_t* handle;  // 用于存储 pcap 句柄
    char errbuf[PCAP_ERRBUF_SIZE];  // 错误信息缓冲区
    struct bpf_program fp;  // 用于过滤表达式
    char filter_exp[] = "tcp port 21 or tcp port 20";
    bpf_u_int32 net = 0;

    // 打开 pcap 文件
    handle = pcap_open_offline("ftp_packets.pcap", errbuf);
    if (handle == NULL) {
        fprintf(stderr, "Couldn't open pcap file: %s\n", errbuf);
        return(2);
    }

    // 编译并应用过滤器
    if (pcap_compile(handle, &fp, filter_exp, 0, net) == -1) {
        fprintf(stderr, "Couldn't parse filter %s: %s\n", filter_exp, pcap_geterr(handle));
        return(2);
    }
    if (pcap_setfilter(handle, &fp) == -1) {
        fprintf(stderr, "Couldn't install filter %s: %s\n", filter_exp, pcap_geterr(handle));
        return(2);
    }

    // 打开日志文件
    FILE* log_file = fopen("ftp_packets_log.csv", "w");
    if (!log_file) {
        perror("File opening failed");
        return EXIT_FAILURE;
    }

    // 写入 CSV 表头
    fprintf(log_file, "Time,Source MAC,Source IP,Destination MAC,Destination IP,Login Name,Password,Status\n");

    // 开始处理捕获的数据包
    pcap_loop(handle, 0, packet_handler, (u_char*)log_file);

    // 清理资源
    fclose(log_file);
    pcap_freecode(&fp);
    pcap_close(handle);

    return 0;
}
